package com.gc.materialdesign.views;

import com.gc.materialdesign.ResourceTable;
import com.gc.materialdesign.utils.ResUtil;
import com.gc.materialdesign.utils.ShapeUtil;
import com.gc.materialdesign.utils.Utils;
import com.gc.materialdesign.utils.ViewHelper;

import ohos.agp.animation.AnimatorProperty;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.DependentLayout;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ElementScatter;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.render.*;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.app.Context;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Size;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

public class Switch extends CustomView implements Component.TouchEventListener {
    private String backgroundColor = "#4CAF50FF";

    private Ball ball;

    private boolean check = false;
    private boolean eventCheck = false;
    private boolean press = false;

    private OnCheckListener onCheckListener;
    private PixelMap bitmap;

    public Switch(Context context) {
        this(context, null);
    }

    public Switch(Context context, AttrSet attrs) {
        this(context, attrs, null);
    }

    public Switch(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setAttributes(context, attrs);
        setTouchEventListener(this);
    }

    // Set atributtes of XML to View
    protected void setAttributes(Context context, AttrSet attrs) {
        // Set size of view
        setMinHeight(Utils.dpToPx(48, context));
        setMinWidth(Utils.dpToPx(80, context));

        // Set background Color
        // Color by resource
        boolean backgroundIsPresent = attrs.getAttr("background").isPresent();
        if (backgroundIsPresent) {
            String color = attrs.getAttr("background").get().getStringValue();
            setBackgroundColor(color);
        }

        boolean checkIsPresent = attrs.getAttr("check").isPresent();
        if (checkIsPresent) {
            check = attrs.getAttr("check").get().getBoolValue();
            eventCheck = check;
        }

        ball = new Ball(context);
        LayoutConfig layoutConfig =
                new DependentLayout.LayoutConfig(Utils.dpToPx(20, context), Utils.dpToPx(20, context));
        layoutConfig.addRule(LayoutConfig.VERTICAL_CENTER, LayoutConfig.TRUE);
        ball.setLayoutConfig(layoutConfig);
        addComponent(ball);
    }

    @Override
    public boolean onTouchEvent(Component component, TouchEvent event) {
        MmiPoint mmiPoint = event.getPointerScreenPosition(event.getIndex());
        if (isEnabled()) {
            isLastTouch = true;
            if (event.getAction() == TouchEvent.PRIMARY_POINT_DOWN) {
                press = true;
            } else if (event.getAction() == TouchEvent.POINT_MOVE) {
                float x = Math.abs(mmiPoint.getX() - getLocationOnScreen()[0]);
                x = Math.max(x, ball.xIni);
                x = Math.min(x, ball.xFin);
                if (x > ball.xCen) {
                    eventCheck = true;
                } else {
                    eventCheck = false;
                }
                ViewHelper.setX(ball, x);
                ball.changeBackground();
                if ((Math.abs(mmiPoint.getX() - getLocationOnScreen()[0]) <= getWidth())) {
                    isLastTouch = false;
                    press = false;
                }
            } else if (event.getAction() == TouchEvent.PRIMARY_POINT_UP || event.getAction() == TouchEvent.CANCEL) {
                press = false;
                isLastTouch = false;
                if (eventCheck != check) {
                    check = eventCheck;
                    if (onCheckListener != null) onCheckListener.onCheck(Switch.this, check);
                }

                if (Math.abs(mmiPoint.getX() - getLocationOnScreen()[0]) <= getWidth()) {
                    ball.animateCheck();
                }
            }
        }
        return true;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        super.onDraw(component, canvas);
        if (!placedBall) {
            placeBall();
        }

        // Crop line to transparent effect
        if (null == bitmap) {
            PixelMap.InitializationOptions initializationOptions = new PixelMap.InitializationOptions();
            initializationOptions.pixelFormat = PixelFormat.ARGB_8888;
            initializationOptions.size = new Size(getWidth(), getHeight());
            bitmap = PixelMap.create(initializationOptions);
        }

        Texture texture = new Texture(bitmap);
        Canvas temp = new Canvas(texture);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(new Color(Color.getIntColor(eventCheck ? Utils.argb(backgroundColor) : "#FFB0B0B0")));
        paint.setStrokeWidth(Utils.dpToPx(2, getContext()));
        temp.drawLine(
                new Point(getHeight() / 2, getHeight() / 2),
                new Point(getWidth() - getHeight() / 2, getHeight() / 2),
                paint);

        Paint transparentPaint = new Paint();
        transparentPaint.setAntiAlias(true);
        transparentPaint.setColor(new Color(ResUtil.getColor(getContext(), ResourceTable.Color_transparent)));
        transparentPaint.setBlendMode(BlendMode.CLEAR);
        temp.drawCircle(
                ViewHelper.getX(ball) + ball.getWidth() / 2,
                ViewHelper.getY(ball) + ball.getHeight() / 2,
                ball.getWidth() / 2,
                transparentPaint);

        canvas.drawPixelMapHolder(new PixelMapHolder(bitmap), 0, 0, new Paint());

        if (press) {
            paint.setColor(new Color(Color.getIntColor(check ? makePressColorString() : "#446D6D6D")));
            canvas.drawCircle(ViewHelper.getX(ball) + ball.getWidth() / 2, getHeight() / 2, getHeight() / 2, paint);
        }
        getContext().getUITaskDispatcher().asyncDispatchBarrier(this::invalidate);
    }

    /**
     * Make a dark color to press effect
     *
     * @return argb
     */
    protected String makePressColorString() {
        int colorInt;
        if (backgroundColor.length() == 7) {
            colorInt = Color.getIntColor(backgroundColor);
        } else {
            colorInt = Color.getIntColor(backgroundColor.substring(0, backgroundColor.length() - 2));
        }
        int r = (colorInt >> 16) & 0xFF;
        int g = (colorInt >> 8) & 0xFF;
        int b = (colorInt >> 0) & 0xFF;
        r = Math.max(r - 30, 0);
        g = Math.max(g - 30, 0);
        b = Math.max(b - 30, 0);
        StringBuilder builder =
                new StringBuilder("#")
                        .append(Integer.toHexString(30))
                        .append(colorPrefix(Integer.toHexString(r)))
                        .append(colorPrefix(Integer.toHexString(g)))
                        .append(colorPrefix(Integer.toHexString(b)));
        return builder.toString();
    }

    private String colorPrefix(String string) {
        return string.length() > 1 ? string : "00";
    }

    // Move ball to first position in view
    boolean placedBall = false;

    private void placeBall() {
        ViewHelper.setX(ball, getHeight() / 2 - ball.getWidth() / 2);
        ball.xIni = ViewHelper.getX(ball);
        ball.xFin = getWidth() - getHeight() / 2 - ball.getWidth() / 2;
        ball.xCen = getWidth() / 2 - ball.getWidth() / 2;
        placedBall = true;
        ball.animateCheck();
    }

    // SETTERS
    public void setBackgroundColor(String colorStr) {
        backgroundColor = colorStr;
        if (isEnabled()) {
            beforeBackground = backgroundColor;
        }
    }

    public void setChecked(boolean check) {
        invalidate();
        this.check = check;
        this.eventCheck = check;
        ball.animateCheck();
    }

    public boolean isCheck() {
        return check;
    }

    class Ball extends Component {
        float xIni, xFin, xCen;

        public Ball(Context context) {
            super(context);
            Element element =
                    ElementScatter.getInstance(context).parse(ResourceTable.Graphic_background_switch_ball_uncheck);
            setBackground(element);
        }

        public void changeBackground() {
            if (eventCheck) {
                ShapeElement element = ShapeUtil.createCircleDrawable(Color.getIntColor(Utils.rgba(backgroundColor)));
                setBackground(element);
            } else {
                Element element =
                        ElementScatter.getInstance(getContext())
                                .parse(ResourceTable.Graphic_background_switch_ball_uncheck);
                setBackground(element);
            }
        }

        public void animateCheck() {
            changeBackground();
            AnimatorProperty animatorProperty = createAnimatorProperty();
            if (eventCheck) {
                animatorProperty.moveFromX(getContentPositionX()).moveToX(ball.xFin);
            } else {
                animatorProperty.moveFromX(getContentPositionX()).moveToX(ball.xIni);
            }
            animatorProperty.setDuration(300);
            animatorProperty.start();
        }
    }

    public void setOnCheckListener(OnCheckListener onCheckListener) {
        this.onCheckListener = onCheckListener;
    }

    public interface OnCheckListener {
        void onCheck(Switch view, boolean check);
    }
}
